#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include <assert.h>

#include "block.h"
#include "stream.h"

struct stream {
    size_t  totalsize;
    block_t *blk_read;
    block_t *blk_write;
    block_t *blk_entry;
};

stream_t *stream_new(void)
{
    stream_t *s  = NULL;
    block_t *blk = NULL;

    s = malloc(sizeof(stream_t));
    if (s) {
        blk = block_new(4096);
        s->totalsize = 0;
        s->blk_read  = blk;
        s->blk_write = blk;
        s->blk_entry = blk;
    }
    return s;
}

void stream_delete(stream_t *s)
{
    if (s && s->blk_entry) {
        block_delete(s->blk_entry);
    }
}

ssize_t stream_write(stream_t *s, uint8_t *wbuf, size_t len)
{
    block_t *blk = NULL;
    block_t *blk_write = NULL;
    size_t total = 0;
    size_t wlen = 0;

    assert(s && wbuf && len);
    blk_write = s->blk_write;

    wlen = block_write(blk_write, wbuf, len);
    total += wlen;
    if (wlen < len) {
        blk = block_new(len - total);
        if (blk) {
            wlen = block_write(blk, wbuf, len-total);
            total += wlen;
            block_append(blk_write, blk);
            s->blk_write = blk;
        } else {
            fprintf(stderr, "%s:%d %s\n", __FILE__, __LINE__, "Out of memory");
        }
    }

    return total;
}

ssize_t stream_read(stream_t *s, uint8_t *rbuf, size_t len)
{
    block_t *blk_read = NULL;
    block_t *blk_prev = NULL;
    size_t rlen = 0;
    size_t total = 0;
    assert(s && rbuf &&len);

    blk_read = s->blk_read;
    while (total < len) {
        rlen = block_read(blk_read, rbuf+total, len-total);
        if (rlen == 0) {
            if (block_isreadable(blk_read))
            {
                sleep(100);
            } else {
                blk_prev = block_prev(blk_read);
                if (!block_isreadable(blk_prev)) {
                    block_reset(blk_prev);
                }
                blk_read = block_next(blk_read);
                s->blk_read = blk_read;
            }
        }
        total += rlen;
    }
    return total;
}


